package org.baojie;

import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.Statements;
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.statement.comment.Comment;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.drop.Drop;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.truncate.Truncate;
import net.sf.jsqlparser.statement.update.Update;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author baojie
 * @date 2022/6/14 下午5:37
 */
public final class ParserState {

    private static final Logger log = LoggerFactory.getLogger(ParserState.class);

    private final Map<CareSentence, List<Statement>> cares = new ConcurrentHashMap<>(16);
    private final ReentrantLock mainLock = new ReentrantLock();
    private final AtomicBoolean hasDone = new AtomicBoolean(false);
    // 需要解析的SQL语句
    private final String sqls;
    // SQL语句对应的数据源信息
    private final SourceDetail detail;
    // 解析的结果
    private Statements states;
    // 出现异常的结果
    private Throwable error;

    private ParserState(String sqls, SourceDetail detail) {
        if (null == sqls || null == detail) {
            throw new NullPointerException();
        }
        this.sqls = sqls;
        this.detail = detail;
        for (CareSentence care : CareSentence.values()) {
            List<Statement> list = new ArrayList<>(32);
            cares.putIfAbsent(care, list);
        }
    }

    public static ParserState create(String sqls, SourceDetail detail) {
        return new ParserState(sqls, detail);
    }

    public boolean classify() {
        boolean done = hasDone.get();
        if (done) {
            return true;
        }
        final ReentrantLock lock = mainLock;
        lock.lock();
        try {
            if (hasDone.get()) {
                return true;
            }
            done = classify0();
            if (done) {
                hasDone.compareAndSet(false, true);
            }
            return done;
        } finally {
            lock.unlock();
        }
    }

    // 进行分类工作
    // 校验语句时候需要先调用这个方法
    public boolean classify0() {
        final Statements copy = getStates();
        if (null == copy) {
            return false;
        }
        final List<Statement> statements = copy.getStatements();
        if (null == statements) {
            return false;
        }
        int size = statements.size();
        if (size <= 0) {
            return false;
        }
        for (int i = 0; i < size; i++) {
            Statement statement = statements.get(i);
            if (statement instanceof CreateTable) {
                CreateTable create = (CreateTable) statement;
                putValue(create, CareSentence.CREATE);
            } else if (statement instanceof Update) {
                Update update = (Update) statement;
                putValue(update, CareSentence.UPDATE);
            } else if (statement instanceof Alter) {
                Alter alter = (Alter) statement;
                putValue(alter, CareSentence.ALTER);
            } else if (statement instanceof Drop) {
                Drop drop = (Drop) statement;
                putValue(drop, CareSentence.DROP);
            } else if (statement instanceof Truncate) {
                Truncate truncate = (Truncate) statement;
                putValue(truncate, CareSentence.TRUNCATE);
            } else if (statement instanceof Delete) {
                Delete delete = (Delete) statement;
                putValue(delete, CareSentence.DELETE);
            } else if (statement instanceof Select) {
                Select select = (Select) statement;
                putValue(select, CareSentence.SELECT);
            } else if (statement instanceof Comment) {
                Comment comment = (Comment) statement;
                putValue(comment, CareSentence.COMMENT);
            } else {
                String sn = statement.getClass().getName();
                log.warn("unknow statement name = " + sn + ", detail = " + detail);
            }
        }
        return true;
    }

    private void putValue(Statement statement, CareSentence care) {
        final List<Statement> contains = cares.get(care);
        contains.add(statement);
    }

    public List<Statement> getCare(CareSentence care) {
        if (null == care) {
            return Collections.emptyList();
        } else {
            int i = 0;
            while (!hasDone.get()) {
                classify();
                if (hasDone.get()) {
                    break;
                } else {
                    if (i >= 100) {
                        break;
                    } else {
                        LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(10, TimeUnit.MILLISECONDS));
                    }
                }
                i = i + 1;
            }
            List<Statement> states = cares.get(care);
            if (null == states) {
                return Collections.emptyList();
            } else {
                return states;
            }
        }
    }

    public String getSqls() {
        return sqls;
    }

    public SourceDetail getDetail() {
        return detail;
    }

    public DataBaseType dbType() {
        return detail.getDataBaseType();
    }

    public Statements getStates() {
        return states;
    }

    public void setStates(Statements states) {
        if (null != states) {
            this.states = states;
        }
    }

    public Throwable getError() {
        return error;
    }

    public void setError(Throwable error) {
        if (null != error) {
            this.error = error;
        }
    }

    @Override
    public String toString() {
        return "ParserState{" +
                "sqls='" + sqls + '\'' +
                ", detail=" + detail +
                ", states=" + states +
                ", error=" + error +
                '}';
    }

}
